import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chenye.mongo.MongoApplication;
import com.chenye.mongo.entity.DataFetchAdaptorResponseMsg;
import com.chenye.mongo.entity.IndexData;
import com.chenye.mongo.entity.UserEntity;
import com.mongodb.DB;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;
import com.mongodb.connection.ClusterDescription;
import com.mongodb.connection.ServerDescription;
import org.bson.BsonDocument;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.mapping.TextScore;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by chenye on 2018/4/15.
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MongoApplication.class})
public class mongoTemplate {
    @Autowired
    private MongoTemplate mongoTemplate;
    private Query query;

    @Test
    public void save(){
        UserEntity userEntity = new UserEntity("chenye",26,"我有一个美丽的梦");
        mongoTemplate.save(userEntity,"chenye");
        userEntity = new UserEntity("chenye",25,"这是一颗美丽的新球");
        mongoTemplate.save(userEntity,"chenye");
    }
    @Test
    public void insert(){
        //批量新增
        List<UserEntity> list = new ArrayList<>();
        list.add(new UserEntity("name1",1,"address1"));
        list.add(new UserEntity("name2",2,"address2"));
        list.add(new UserEntity("name3",3,"address3"));
        mongoTemplate.insert(list,UserEntity.class);
    }
    @Test
    public void query(){
        Query query = new Query(Criteria.where("name").is("chenye")).addCriteria(Criteria.where("age").is(26));
        UserEntity userEntity = mongoTemplate.findOne(query,UserEntity.class,"chenye");
        System.err.println(userEntity.toString());
    }
    @Test
    public void upsert(){
        //批量更新
        //如果没有匹配到对应的数据则根据条件和set值，新增一条
        Query query = new Query(Criteria.where("age").is(10));
        Update update = new Update();
        update.set("name","123");
//        mongoTemplate.upsert(query,update,UserEntity.class);
        mongoTemplate.upsert(query,update,"chenye");
        WriteResult writeResult =  mongoTemplate.updateMulti(query,update,UserEntity.class);
        System.err.println(writeResult.toString());
    }
    @Test
    public void update(){
        //批量更新
        Query query = new Query(Criteria.where("name").is("123"));
        Update update = new Update();
        update.set("name","456");
        mongoTemplate.updateMulti(query,update,"userEntity");
    }
    @Test
    public void delete(){
        Query query = new Query(Criteria.where("address").is("我有一个美丽的梦"));
        mongoTemplate.remove(query,"chenye");
        List<UserEntity> list = mongoTemplate.find(new Query(Criteria.where("name").is("chenye")),UserEntity.class,"chenye");
        System.err.println(list.toString());
    }

    @Test
    public void collectionExists(){
        System.err.println(mongoTemplate.collectionExists("chenye"));
        System.err.println(mongoTemplate.collectionExists("chenye1"));
    }

    @Test
    public void createCollectionExists(){
        mongoTemplate.createCollection("createCollection");
        System.err.println(mongoTemplate.collectionExists("createCollection"));
    }
    @Test
    public void getDb(){
        System.err.println(mongoTemplate.getDb());
    }

    //分页、模糊匹配
    @Test
    public void page(){
        Query query = Query.query(Criteria.where("name").regex(Pattern.compile(".*?chenye.*")));
        // 每页五个
        Pageable pageable = new PageRequest(0, 5); // get 5 profiles on a page
        query.with(pageable);
        // 排序
        query.with(new Sort(Sort.Direction.DESC, "age"));
        // 查询总数
        int count = (int) mongoTemplate.count(query, HashMap.class, "chenye");
        List<HashMap> items = mongoTemplate.find(query, HashMap.class, "chenye");
        // System.out.println("stories:" + stories.size() + " count:" + count);
        Page<HashMap> page = PageableExecutionUtils.getPage(items, pageable, () -> count);
        System.err.println(page.getContent());
        System.err.println(page.getTotalPages());

    }

    @Test
    public void testAgg(){
        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(Aggregation.match(Criteria.where("").is("")));
        operations.add(Aggregation.match(Criteria.where("").is("")));
//        AggregationExpression expression = AggregationExpression
        Set<String> set = new HashSet<>();
        operations.add(Aggregation.group(""));
//        operations.add(Aggregation.project("body"));
        Aggregation aggregation = Aggregation.newAggregation(operations);
        AggregationResults<String> results = mongoTemplate.aggregate(aggregation,"",String.class);
        List<String> list = results.getMappedResults();
        System.out.println(123);
    }

    //过滤满足条件的数组
    @Test
    public void testArrayQuery(){
        /*
         * db.getCollection('indexData').find({"array":{$elemMatch:{$in:['a']}}})
         *
         * */
        String collectionName = "indexData";
        Query query = new Query(Criteria.where("array").elemMatch(Criteria.where("$in").is(new String[]{"a"})));
        Map<String,Object> map = mongoTemplate.findOne(query,HashMap.class,collectionName);
        System.err.println(map);
    }
    //嵌套查询
    @Test
    public void testContainQuery(){
        /*
         * db.getCollection('indexData').find({"10027.status":200})
         * 需要注意查询条件的类型，如果类型不对，会查询不出结果的。
         * */
        String collectionName = "indexData";
        Query query = new Query(Criteria.where("10027.status").is(200));
        Map<String,Object> map = mongoTemplate.findOne(query,HashMap.class,collectionName);
        System.err.println(map);
    }

    /**
     * mongodb 读写分离设置,亲测有效,建议自己在写一个Template初始化bean，设置该属性
     */
    @Test
    public void querySeparation(){
        mongoTemplate.setReadPreference(ReadPreference.secondaryPreferred());
        String collectionName = "indexData";
        Query query = new Query(Criteria.where("orderId").is("4343232dfdf13dsf"));
        Map<String,Object> map = mongoTemplate.findOne(query,HashMap.class,collectionName);
        System.err.println(map);

        map = new HashMap();
        map.put("test","yes");
        map.put("name","yes");
        map.put("test","yes");
        mongoTemplate.save(map,collectionName);

    }


    @Test
    public void getClearData(){
        Runtime run = Runtime.getRuntime();
        System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + (run.totalMemory()-run.freeMemory()) );
        run.gc();
        System.out.println("time: " + (new Date()));
        // 获取开始时内存使用量
        long startMem = run.totalMemory()-run.freeMemory();
        System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + startMem );
        long time = System.currentTimeMillis();
        Query query = Query.query(Criteria.where("").is(""))
                .addCriteria(Criteria.where("").is(""))
                .addCriteria(Criteria.where("").regex(Pattern.compile("")));
        String collectionName = "";
        long count = mongoTemplate.count(query,HashMap.class,collectionName);
        String mapFunction =  "function(){"
                + "emit(this.age,{'name':this.name});" +"}";

        String reduceFunction =  "function(key, values)" +"{var sum = 0;"
                + "var test='';" +"for(i in values){" +"test+=i;" +"}" +""
                + "values.forEach(function(doc){" +"sum += 1; }); "
                + "return {persons:sum,test:test,values:values};" +"};";
        MapReduceResults results = mongoTemplate.mapReduce(query,collectionName,mapFunction,reduceFunction,HashMap.class);
        Iterator<HashMap> iterator = results.iterator();
        while(iterator.hasNext()){
            HashMap map = iterator.next();
        }

        System.out.println(count);
        System.out.println("time:"+(System.currentTimeMillis()-time));


        long endMem = run.totalMemory()-run.freeMemory();
        System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + endMem );
        System.out.println("memory difference:" + (endMem-startMem));
    }

    @Test
    public void getRegexResource() throws IOException {
        Runtime run = Runtime.getRuntime();
        System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + (run.totalMemory()-run.freeMemory()) );
        run.gc();
        System.out.println("time: " + (new Date()));
        // 获取开始时内存使用量
        long startMem = run.totalMemory()-run.freeMemory();
        System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + startMem );
        long time = System.currentTimeMillis();


        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(Aggregation.match(Criteria.where("").is("")));
        operations.add(Aggregation.match(Criteria.where("").is("")));
        operations.add(Aggregation.group("",""));


        Aggregation aggregation = Aggregation.newAggregation(operations);
        mongoTemplate.aggregate(aggregation,"",String.class);
//        this.getKeyWordCount(strList,keyWords);

        System.out.println("time: " + (new Date()));
        System.out.println("time:"+(System.currentTimeMillis()-time));
        long endMem = run.totalMemory()-run.freeMemory();
        System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + endMem );
        System.out.println("memory difference:" + (endMem-startMem));
    }


    public void testDecision(){
        String content = getCheckContent("app_message");
        Set<String> keyWords = getKeyWords("message");
        Map<String,Integer> map = countKeyWord(content,keyWords);
        System.out.println(count(map));
    }

    /**
     * 获取需要检测的数据源数据
     * @param collectionName
     * @return
     */
    public String getCheckContent(String collectionName){
        Query query = Query.query(Criteria.where("").is(""))
                .addCriteria(Criteria.where("").is(""))
                .addCriteria(Criteria.where("").regex(Pattern.compile("")));
        List<HashMap> queryResults = mongoTemplate.find(query,HashMap.class,collectionName);
        if(queryResults!=null && queryResults.size()>0){
            StringBuffer sb = new StringBuffer();
            for(HashMap result : queryResults){
                sb.append(result.get(""));
            }
            return sb.toString();
        }
        return null;
    }

    /**
     * 通过类型获取对应的关键字
     * @param
     * @return
     */
    public Set<String> getKeyWords(String kw){
        //TODO 先通过数据库获取配置好的关键字
//        String kw = "";
        String[] kwArray = kw.split("\\|");
        Set<String> keyWordRawData = new HashSet<>(Arrays.asList(kwArray));
        Set<String> regexSet = new HashSet<>(kwArray.length);
        for(String str : keyWordRawData){
            regexSet.add(constructRegex(str));
        }
        return regexSet;
    }
    @Test
    public void testConstructRegex(){

    }

    /**
     * 包含+字符的词组生成对应的正则表达式
     * @param keyWord
     * @return
     */
    public String constructRegex(String keyWord){
        if(!keyWord.contains("+")){
            return keyWord;
        }
        String[] strArray = keyWord.split("\\+");
        int size = 0;
        for(String str : strArray){
            size += str.length();
        }
        Set<String> set = new HashSet<>();
        StringBuffer sb = new StringBuffer();
        getAllPossibleCombinations(strArray,size,sb,set);
        //(已拖欠)(.*)(天)|(天)(.*)(已拖欠)
        for(String str : set){
            strArray = str.split("\\|");
            for(String s : strArray){
                sb.append("(").append(s).append(")(.*)");
            }
            sb.delete(sb.length()-4,sb.length());
            sb.append("|");
        }
        sb.delete(sb.length()-1,sb.length());
        System.out.println(set.size());
        set = null;
        strArray = null;
        return sb.toString();
    }

    /**
     * 获取所有词 组成的可能
     * @param s
     * @param len
     * @param sb
     * @param set
     * @return
     */
    public void getAllPossibleCombinations(String[] s,int len,StringBuffer sb,Set<String> set){
        if(sb.length() >= len) {
            set.add(sb.toString());
        }
        else {
            for(int i = 0,length = s.length; i < length; i++){
                String current = s[i];
                if(sb.toString().contains(current)){
                    continue;
                }
                sb.append(current).append("|");
                getAllPossibleCombinations(s,len+1,sb,set);
                sb.delete(sb.length()-(current.length()+1),sb.length());
            }
        }
    }

    /**
     * 统计组合词出现的次数
     * 规则是按照组合词同时出现时，其中单个次最小的次数为准
     * @param content
     * @param keyWord
     * @return
     */
    public Integer countCombinationKeyWord(String content,String keyWord){
        String[] kwArray = keyWord.split("\\+");
        Map<String,Integer> map = new HashMap<>();
        for(String kw : kwArray){
            map.put(kw,matchCount(content,keyWord));
        }
        return map.values().stream().min((v1,v2)->v1.compareTo(v2)).get();
    }

    /**
     * 统计关键字出现次数
     * @param content
     * @param keyWords
     * @return
     */
    public Map<String,Integer> countKeyWord(String content,Set<String> keyWords){
        if(null==keyWords || keyWords.size()==0){
            throw new RuntimeException("关键字不能为空!");
        }
        //init count num
        Map<String,Integer> map = new HashMap<>();
        for(String keyWord : keyWords){
            map.put(keyWord,0);
        }
        if(StringUtils.hasText(content)){
            for(String keyWord : keyWords){
                if(keyWord.contains("+")){
                    //组合词处理
                    map.put(keyWord,countCombinationKeyWord(content,keyWord));
                }else{
                    map.put(keyWord,map.get(keyWord)+matchCount(content,keyWord));
                }
            }
        }
        map.forEach((k, v) -> System.out.println(k + ":" + v));
        return map;
    }


    @Test
    public void testConcatKeyWord1(){
        long time = System.currentTimeMillis();
        List<String> contents = new ArrayList<>();
        contents.add("ABAC");
        contents.add("ABAB");
        contents.add("ABB");
        Set<String> keyWords = new HashSet<>();
        keyWords.add("A+B+C");
        countSMSKeyWord(contents,keyWords);
        System.out.println(System.currentTimeMillis()-time);
    }
    /**
     * 统计手机短信关键字
     * @param contents
     * @param keyWords
     * @return
     */
    public Map<String,Integer> countSMSKeyWord(List<String> contents,Set<String> keyWords){
        if(null==keyWords || keyWords.size()==0){
            throw new RuntimeException("关键字不能为空!");
        }
        //init count num
        Map<String,Integer> map = new HashMap<>();
        for(String keyWord : keyWords){
            map.put(keyWord,0);
        }
        if(contents!=null && contents.size()>0){
            int count;
            for(String keyWord : keyWords){
                count = 0;
                for(String content : contents){
                    if(keyWord.contains("+")){
                        //组合词处理
                        count += countCombinationKeyWord(content,keyWord);
                    }else{
                        count = matchCount(content,keyWord);
                    }
                }
                map.put(keyWord,map.get(keyWord)+count);
            }
        }
        map.forEach((k, v) -> System.out.println(k + ":" + v));
        return map;
    }

    /**
     * 统计关键字匹配次数
     * @param content
     * @param keyWord
     * @return
     */
    public Integer matchCount(String content,String keyWord){
        int count = 0;
        Pattern pattern = Pattern.compile(keyWord);
        Matcher matcher = pattern.matcher(content);
        while(matcher.find()){
            count++;
        }
        return count;
    }
    @Test
    public void testMapStream(){
        Map<String,Integer> map = new HashMap<>();
        map.put("2",2);
        map.put("3",3);
        map.put("4",4);
        map.put("1",1);
        System.out.println(map.values().stream().min((v1,v2)->v1.compareTo(v2)).get());
        System.out.println(map.values().stream().max((v1,v2)->v1.compareTo(v2)).get());
    }

    /**
     * 关键字次数总和统计
     * @param countKeyWords
     * @return
     */
    public Integer count(Map<String,Integer> countKeyWords){
        int num = 0;
        Iterator<Integer> iterator = countKeyWords.values().iterator();
        while(iterator.hasNext()){
            num += iterator.next();
        }
        return num;
    }



    @Test
    public void test(){
        Query query = new Query(Criteria.where("orderId").is("20180720_002")).addCriteria(Criteria.where("$isolated").is(1));

        mongoTemplate.findOne(query,Map.class,"indexData");

        Update update = new Update();
        update.set("orderId", "20180720_002");
        update.set("updateTime", System.currentTimeMillis());
        update.set("appKey","lemontest");

        Map data=new HashMap();
        data.put("status",200);
        data.put("productId",11023);
        data.put("msg",null);
        data.put("notifyUrl",null);
        data.put("productName","利信利薪模型指标获取接口");
        data.put("orderId","20180720_002");
        Map<Integer,Object> map = new HashMap<>();
        map.put(2755,"1");
        map.put(2756,"1");
        map.put(2757,"4");
        map.put(2758,"3");
        map.put(2760,10);
        map.put(2761,"1");
        map.put(2762,10000.0);
        map.put(2763,5000.0);
        map.put(2764,1000.0);
        map.put(2765,"80");
        map.put(2766,"1");
        map.put(2767,"-1");
        map.put(2768,1);
        map.put(2769,1);
        map.put(2770,1);
        map.put(2771,1);
        data.put("data",map);
        update.set("11023",data);
        WriteResult writeResult = mongoTemplate.upsert(query, update, IndexData.class);

    }
    @Test
    public void testIndexData(){
        Query query = new Query(Criteria.where("orderId").is("chenye20180731-13").and("appKey").is("lemontest"));
        Map map = mongoTemplate.findOne(query,HashMap.class,"indexData");

        map.get("11023");
        map.get("11021");
        String json = JSONObject.toJSONString(map.get("11021"));
        System.err.println(json);
        DataFetchAdaptorResponseMsg data = JSON.parseObject(json,DataFetchAdaptorResponseMsg.class);
    }
    @Test
    public void testBreak(){
        List<Integer> list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        for(Integer num : list){
            con:
            if(num==1){
                break con;
            }
            System.err.println(num);
        }
    }
    @Test
    public void testPrint(){
        System.err.println(mongoTemplate.toString());
        DB db = mongoTemplate.getDb();
        System.err.println(db.getName());
        System.err.println();
    }

}
